Dedicated PTY supervisor
~~~~~~~~~~~~~~~~~~~~~~~~
See ../../doc/vtvstty.txt for context and some general ideas regarding
terminals and terminal applications.

There are at least three unrelated problems that could be nicely addressed
by having a PTY supervisor:

    * distinct privilege levels for terminal emulators and the apps
    * detachable sessions (think screen, tmux, dtach)
    * restricted access to /dev/ptmx (and /dev in general)

The service pre-allocates PTY pairs for the children its spawns, and keeps
the fds open when a session gets detached. The actual terminal emulators,
that is, the GUI apps that render the characters, do not need to deal with
PTY allocation and/or process supervision. The spawned applications get
an open slave side PTY on their fds 0 and 1. The emulators request a copy
of the master fd over the control channel and use it to communicate with
the VT application running under ptyhub.

PTY fd handling is the part that sets ptyhub aside from apphub.
Pretty much everything else is the same between the two services.


How detaching works
~~~~~~~~~~~~~~~~~~~
Imagine the terminal is not an emulator but a real VT220. Switch it off.
The session is now detached. Switch it back on. The session is re-attached.
VT220 does not keep its state across a power cycle, it powers up with an
empty screen and re-initialized registers.

Now with emulators. During normal operation, the terminal emulator has a copy
of the master side PTY fd and uses it to communicate with the application.
Another copy is kept within ptyhub, idle. It's not getting polled or read from.

To detach a session, the terminal closes its copy of the master side fd and
notifies ptyhub that it has detached (either via a command, or by closing the
control fd). Once that happens, ptyhub stars polling the master side fd and
sinking any readable data there, emulating an open port with no flow control.
Whatever the application is sending to the port during this time gets lost.

When a session gets re-attached, ptyhub sends a copy of the same master fd
to the new terminal emulators, and stops polling the one it keeps. From this
point on, the terminal is expected to receive whatever the application is
sending to the port.


Restoring contents for re-attached sessions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Neither ptyhub nor terminal emulators are expected to persist any screen
contents. Instead, persistency is pushed onto the application, which should
be able to re-draw itself at any time on request.

Newly-attached terminal sets terminal size (TIOCSWINSZ), triggering SIGWINCH
for the app and forcing it to redraw the screen, effectively restoring the
user-visible image. This happens pretty much naturally, as the code that makes
this work is exactly the code that normally handles terminal resizing.

The only catch with the SIGWINCH trick is that Linux only sends SIGWINCH
when the newly-set size differs from current values. A naive implementation
would skip re-draw if the attached terminal happens to be of the same size
as the detached one. To mend this, detached sessions get their window size
reset to (0,0) by ptyhub. The application may use the zero size as a hint
that it's been detached, stopping unnecessary screen updates.


The control connection
~~~~~~~~~~~~~~~~~~~~~~
Terminal emulator attaching to a session opens a control connection to ptyhub,
and sends attach command. In reply, it gets a dup'ed copy of the master side
fd for the PTY. For the remainder of the session, the terminal uses the master
fd to communicate with the application, but also keeps the control connection
open. Since ptyhub has no obvious way of knowing what happens with the master
side fd it sent to the terminal, it has to assume the master fd is in use as
long as the terminal process is alive, and uses the control connection as a
sort of dead man switch to get notified in case the terminal process dies.

The control connection is also used for signalling the application and passing
exit notifications but the primary reason for it to be open is to let ptyhub
know the terminal is still alive.

Terminating control connection to ptyhub auto-detaches the session associated
with that control connection. This way, a crash of the terminal emulator does
not kill the (possibly important) application running under ptyhub, and gives
the user a chance to re-attach another terminal.

There is a potentially baffling consequence: closing the terminal by external
means (killing the terminal) does not kill the application. To kill the app
itself, the user has to command the application to quit (C-d for shell,
something like q for non-shell applications). The application would quit,
ptyhub would then send an exit notification to the terminal, prompting a
properly written terminal to close.

Terminal emulator may request the app to get killed when attempt is made
to close the terminal in a particular way (Alt+F4), but this has to be done
explicitly in the terminal emulator.


Compatible terminal
~~~~~~~~~~~~~~~~~~~
This service is meant to be used with terminal emulators written specifically
to work with it. GNU/Linux style terminal emulators like xterm, rxvt etc are
not generally usable.

The immediate reason for this is that the terminal needs to get its copy
of the PTY master fd from ptyhub, which involves talking the ptyhub control
protocol, and there is no way of doing it other than in the terminal itself.

A deeper cause however is that the whole approach to handling terminal
application (which ptyhub is just a part of) requires a different kind
of terminal emulators. There is no point in trying to solve the PTY
handover issue to let GNU-style hybrid terminals work with ptyhub because
they won't make good VT emulators anyway.

There is a shim called `nestvt` here which attempts to emulate a ptyhub
compatible terminal on a GNU-style terminal, but it is mostly written for
testing and development purposes and not meant for routine use. It is also
inherently inefficient; using nestvt means allocating two PTYs for each
application, and having an extra process around that basically just pipes
data between the application and the terminal.


PTY mastering issues
~~~~~~~~~~~~~~~~~~~~
As implemented here, the terminal gets a copy of the PTY master side fd
from ptyhub over the control channel, and from that point on, communicates
directly with the backing application through the PTY. For the most part,
the hub does not get involved at all. This scheme basically just replaces
open("/dev/ptmx") and a couple of ioctls with a bunch of socket syscalls,
without any changes to the core terminal operation.

The problem with this approach is that ptyhub cannot forcefully detach
a terminal. It has to rely on the terminal to close its copy of the master
fd when detaching. This approach also requires two channels for each
terminal-application pair: one between the terminal and the application (the
PTY fd) and the other between the terminal and the hub (the control channel).

The obvious alternative approach is to have the hub act as a proxy between
terminals and applications, completely isolating the terminal from ever
touching the master side fd. With this approach, the terminal would get
application data through the control channel and send keystrokes back over
that channel as well. No need for the second channel, and no need to trust
the terminals since the hub can easily close the control connection.

The alternative approach has been rejected however. Having a proxy process
increases latency for the terminal applications, and incurs a lot of
unnecessary copying between the control channels and the PTY fds which
has to happen in the hub. The whole idea goes against the way PTYs are
implemented in Linux. This approach also loads the hub process proportionally
to the amount of data passed between *all* active terminal applications,
while with the implemented approach, the load on the hub does not depend
on normal app activity at all.  The downsides of having a extra (mostly idle)
fd were deemed minor in comparison to having potentially heavily loaded
latency-critical code path.


Handling stderr
~~~~~~~~~~~~~~~
Unlike with GNU-style terminals, applications running under ptyhub do not have
their stderr (fd 2) pointing to the slave side of the PTY. Instead, anything
written to stderr gets captured in a ring buffer just like it's done in apphub.

This way, VT applications can use stderr for error messages, without having
to worry about said messages messing up user visible screen contents on the
terminal.

Like with apphub, the user can examine the contents of the ring buffer, both
while the application is running and after it has died.
